{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 面向对象的编程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 什么是面向对象编程？\n",
    "2. 如何定义类\n",
    "3. 如何创建对象\n",
    "4. 如何定义方法和属性\n",
    "5. 方法调用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 什么是面向对象编程 Object Oriented Programming， OOP\n",
    "\n",
    "- 面向对象是一种**程序设计思想**。OOP把对象作为程序的基本单元，一个对象包含了数据和操作数据的函数。\n",
    "\n",
    "- 主流的编程范式：\n",
    "\n",
    "  - PP，**面向过程**，C语言，自顶向下，抽象成一个个过程\n",
    "  - IP，面向指令，汇编语言，更接近于机器，把我们的世界抽象成指令\n",
    "  - FP, 函数式编程，要区分过程和函数，函数式编程要求相同的输入会产生相同的输出\n",
    "  - LP，面向逻辑的编程，现实中用到的语言比较少，把世界抽象成与或非\n",
    "  - OOP, **面向对象的编程**\n",
    "  - AOP，面向切面编程，如python的装饰器，是来解决一类问题的，再如sql,用来解决数据库的查询\n",
    "\n",
    "- 面向过程的程序设计：把**计算机程序视为一系列的命令集合**，即一组函数的**顺序执行**。为了简化程序设计，面向过程把函数继续切分为子函数，即把大块函数通过切割成小块函数来降低系统的复杂度。\n",
    "  - 函数的调用集合\n",
    "\n",
    "  \n",
    "- 面向对象的程序设计：把**计算机程序视为一组对象的集合**，而每个对象都可以接收其他对象发过来的消息，并处理这些消息，计算机程序的执行就是一系列消息在各个对象之间传递。\n",
    "  - 对象之间传递信息的集合\n",
    "\n",
    "  - 在Pytho中，所有数据类型都可以视为对象，也可以自定义对象。\n",
    "    - 自定义的对象数据类型就是面向对象中的**类(Class)**的概念。 \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 面向对象 v.s. 面向过程\n",
    "\n",
    "- 需求： 存储学生成绩和打印学生成绩\n",
    "我们以一个例子来说明面向过程和面向对象在程序流程上的不同之处。\n",
    "假设我们要处理学生的成绩表，为了表示一个学生的成绩，面向过程的程序可以用一个dct表示："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 面向过程\n",
    "\n",
    "- 思考程序的执行流程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "贾东晓: 97\n",
      "刘星宇: 97\n"
     ]
    }
   ],
   "source": [
    "# 使用函数将学生姓名和成绩用dict表示\n",
    "def score(name, score):\n",
    "    return {'name': name, 'score': score}\n",
    "\n",
    "# 使用函数输出学生成绩\n",
    "def print_score(stu):\n",
    "    print('{m[name]}: {m[score]}'.format(m = stu))\n",
    "\n",
    "\n",
    "\n",
    "stu1 = score('贾东晓', 97)\n",
    "stu2 = score('刘星宇', 97)\n",
    "\n",
    "print_score(stu1)\n",
    "print_score(stu2)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 面向对象\n",
    "\n",
    "- 把每一位学生的数据类型视为一个对象\n",
    "  - 需要先定义一个Student类\n",
    "  - 每一位学生为类的实例化\n",
    "- 对象包含 属性 和 方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "贾东晓: 97\n",
      "刘星宇: 97\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "('贾东晓', 97)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Student:\n",
    "\n",
    "    def __init__(self, name, score):   \n",
    "        #类的初始化，给对象传入参数\n",
    "        self.name = name   # 属性\n",
    "        self.score = score\n",
    "\n",
    "    def print_score(self):  # 方法\n",
    "        print('{}: {}'.format(self.name, self.score))\n",
    "\n",
    "\n",
    "# 创建一个student对象（类的实例化, Instance）\n",
    "jiadongxiao = Student('贾东晓', 97)  #类的一个实例\n",
    "liuxingyu = Student('刘星宇', 97)\n",
    "\n",
    "jiadongxiao.print_score()\n",
    "liuxingyu.print_score()     \n",
    "\n",
    "jiadongxiao.name, jiadongxiao.score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- OOP的三大特征：\n",
    "  - **封装**\n",
    "    - 我有一些想法，不想告诉你们，这就是封装，需要通过一些约定，然后才能获知\n",
    "  - **继承**\n",
    "    - 相当于继承父母的基因\n",
    "  - **多态**\n",
    "    - 比如，我们都是人，有的人会python,有的人会java, 有的人会跳舞... "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类和对象"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **类**(Class)：是一种抽象的概念，是具有共同特征和行为事物的统称 （具有相以内部状态和运动规律的实体的集合（或称为抽象））\n",
    "    - i.e. 刚才定义的 Student类 \n",
    "      - 相同的特征：有姓名和成绩\n",
    "      - 相同的行为：输出成绩\n",
    "  \n",
    "- **对象**：是类的一个具体的存在\n",
    "  - i.e. 由Student类创建的每一位学生的变量就是对象 \n",
    "    - 由类创建具体的对象，可以称为类的实例化\n",
    "  - 一个类可以有多个对象\n",
    "\n",
    "\n",
    "- i.e. 类相当于制造飞机时的图纸，用它来创建的飞机就相当于对象。\n",
    "- i.e. `list` --- 类， 创建的列表 `a = list(`abc`)` --- 对象"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://tingwen-pic.oss-cn-beijing.aliyuncs.com/img/20220330010941.png)\n",
    "\n",
    "<br>\n",
    "\n",
    "![](https://tingwen-pic.oss-cn-beijing.aliyuncs.com/img/20220330011033.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义类和创建对象"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 定义类\n",
    "\n",
    "- 使用`class`关键字来定义一个类\n",
    "```python\n",
    "class Student:\n",
    "    \n",
    "  def __init__(self):\n",
    "      self.name = '杨博闻'\n",
    "      self.grade = 97\n",
    "```\n",
    "\n",
    "```python\n",
    "class Student(object):\n",
    "  \n",
    "  def __init__(self):\n",
    "    self.name = '杨博闻'\n",
    "    self.grade = 97\n",
    "  \n",
    "  def hey(self):\n",
    "    print('Hey, {}'.format(self.name))\n",
    "\n",
    "```\n",
    "\n",
    "#### 创建对象\n",
    "\n",
    "- `对象名 = 类名()`   括号内传入实例化类的参数（如有）\n",
    "- `xiaoyang = Student()`\n",
    "\n",
    "\n",
    "- object 是python中所有类的最顶级父类；\n",
    "- 类名 的命名规则是“大驼峰命名法”；\n",
    "- `__init__` 和`info`是在类中定义的方法\n",
    "  - 第一个参数一般是`self`，表示实例对象本身，也可以将self换成任何关键字，python会将传入的第一个参数视为对象本身(只认位置，不认名字)\n",
    "\n",
    "#### 使用 . （成员运算符）获取对象的属性或方法\n",
    "- `xiaoyang.name`\n",
    "- `xiaoyang.hey()`\n",
    "- 调用方法时，不需要传递self参数，python解释器会自动把当前的对象引用传递过去。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "杨博闻\n",
      "Hey, 杨博闻\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "  def __init__(self):\n",
    "    self.name = '杨博闻'\n",
    "    self.grade = 97\n",
    "  \n",
    "  def hey(self):\n",
    "    print('Hey, {}'.format(self.name))\n",
    "\n",
    "xiaoyang = Student()\n",
    "print(xiaoyang.name)\n",
    "xiaoyang.hey()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### init方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `__init__`方法，在创建一个对象时默认被自动调用\n",
    "  - 实例化类（初始化类）的过程就是在执行`__init__`函数\n",
    "- 如果类中没有写`__init__`方法，Python会自动创建，但是不执行任何操作;\n",
    "- 如果在类初始化时完成一些功能，可以自己定义`__init__`方法;\n",
    "- 一个类中，无论自己是否定义了`__init__`方法，一定会有`__init__`方法。\n",
    "\n",
    "```{note}\n",
    "Python的类里提供的，两个下划线开始，两个下划线结束的方法，可以成为魔法方法，`__init__`就是一个魔法方法，通常用来做属性初始化或赋值操作。\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我正在被执行\n",
      "over\n",
      "2410500461280\n",
      "where am I? I am at 2410500461280\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "# check: 实例化类（初始化类）的时候会执行`__init__`函数\n",
    "class Test:\n",
    "    def __init__(self):\n",
    "        print('我正在被执行')\n",
    "        print('over')\n",
    "\n",
    "test = Test()  #类的初始化\n",
    "\n",
    "\n",
    "# check: `self`表示实例对象本身\n",
    "class Test2:  \n",
    "\n",
    "    def __init__(self):\n",
    "        self.place = id(self)\n",
    "\n",
    "    def location(self):\n",
    "        print('where am I? I am at {}'.format(id(self)))\n",
    "        \n",
    "test = Test2()\n",
    "print(id(test))\n",
    "test.location()\n",
    "print(id(test) == test.place)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 有参数的`init`方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hey, 杨博闻! Congrats, you got 97!\n",
      "Hey, 杨博闻! Congrats, you got 97!\n"
     ]
    }
   ],
   "source": [
    "# 没有给 __init__()传入参数：\n",
    "class Student(object):\n",
    "  \n",
    "  def __init__(self):\n",
    "    self.name = '杨博闻'\n",
    "    self.grade = 97\n",
    "  \n",
    "  def hey(self):\n",
    "    print('Hey, {}! Congrats, you got {}!'.format(self.name, self.grade))\n",
    "\n",
    "xiaoyang = Student()\n",
    "xiaoli = Student()\n",
    "\n",
    "xiaoyang.hey()  # Hey, 杨博闻\n",
    "xiaoli.hey()  # Hey, 杨博闻  和xiaoyang 具有相同的属性\n",
    "\n",
    "# 需求：name 和 grade 在初始化时可以传入不同的值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-  method 1: 手动修改\n",
    "   - 创建对象后，手动使用`对象名.变量名 = xx` 定义属性 (python 可以动态的给对象增减属性)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hey, 杨博闻! Congrats, you got 97!\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "    \n",
    "  def hey(self):\n",
    "    print('Hey, {}! Congrats, you got {}!'.format(self.name, self.grade))\n",
    "\n",
    "xiaoyang = Student()\n",
    "xiaoli = Student()\n",
    "\n",
    "xiaoyang.name = '杨博闻'\n",
    "xiaoyang.grade = 97\n",
    "\n",
    "xiaoyang.hey()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- method2：给 init 函数增加形参，并在函数内定义属性\n",
    "  - 在类初始化的时候传入实参，定义属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hey, 杨博闻! Congrats, you got 97!\n",
      "Hey, 李佳静! Congrats, you got 97!\n",
      "2410512051920\n",
      "2410512051344\n",
      "Hey, 杨博闻! Congrats, you got 97!\n",
      "140707727285464\n",
      "Hey, 李佳静! Congrats, you got 97!\n",
      "140707727285464\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "  \n",
    "  def __init__(self, name, grade):\n",
    "    self.name = name\n",
    "    self.grade = grade\n",
    "  \n",
    "  def hey(self):\n",
    "    print('Hey, {}! Congrats, you got {}!'.format(self.name, self.grade))\n",
    "\n",
    "xiaoyang = Student('杨博闻',97)\n",
    "xiaoli = Student('李佳静', 97)\n",
    "\n",
    "xiaoyang.hey()  # Hey, 杨博闻\n",
    "xiaoli.hey()\n",
    "\n",
    "#################\n",
    "# 不用对象的属性值单独保存； 同一类的不同对象的实例方法共享内存\n",
    "print(id(xiaoyang.name))\n",
    "print(id(xiaoli.name))\n",
    "\n",
    "print(id(xiaoyang.hey()))\n",
    "print(id(xiaoli.hey()))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 小总结\n",
    "\n",
    "    - 通过一个类，可以创建多个对象\n",
    "    - `__init__()`中，第1个形参为self，如果在创建对像时传递了2个实参，那么`__init__()`中出了除了self外还需要2个形参，例如`__init__(self,x,y)`\n",
    "    - 在类内部获取属性和实例方法，通过self获取；\n",
    "    - 在类外部获取属性和实例方法，通过对象名获取。\n",
    "    - 将方法/函数放在类中的好处：\n",
    "      - 防止命名空间冲突\n",
    "      - 类的所有实例都可以调用定义的函数\n",
    "    - 如果一个类有多个对象，每个对象的属性是各自保存的，都有各自独立的地址；\n",
    "    - 但是实例方法是所有对象共享的，只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类变量和实例变量\n",
    "\n",
    "- 类的直接下级作用域定义的变量，叫作类变量\n",
    "  - 类变量对类和实例对象都可见\n",
    "- 关联到实例对象的变量，叫作实例变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "windy\n",
      "windy\n",
      "windy\n",
      "rainy\n",
      "sunny\n",
      "sunny\n",
      "windy\n",
      "sunny\n"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    STATUS = 'windy'  # 类变量，类本身和类的对象都可以调用\n",
    "    \n",
    "    def __init__(self, status):\n",
    "        self.status = status  # 实例变量\n",
    "\n",
    "# 创建对象\n",
    "a = A('rainy')\n",
    "b = A('sunny')\n",
    "\n",
    "#A.STATUS v.s. a.STATUS\n",
    "print(a.STATUS)\n",
    "print(b.STATUS)\n",
    "print(A.STATUS) \n",
    "# 类变量：类本身和类的对象都可以调用； 所有实例共享类变量\n",
    "\n",
    "# a.status v.s. A.status\n",
    "print(a.status)\n",
    "# print(A.status)\n",
    "# 实例变量：只能对象调用，类本身不能调用\n",
    "\n",
    "# 类变量可以修改\n",
    "A.STATUS='sunny'\n",
    "print(a.STATUS)\n",
    "print(b.STATUS)\n",
    "\n",
    "# a.STATUS, b.STATUS ? \n",
    "a.status='windy'\n",
    "print(a.status)\n",
    "print(b.status)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- NOTE: 当我们增加一个对象的属性时，如果属性的名字和类变量相同，则会覆盖类变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cool\n",
      "sunny\n"
     ]
    }
   ],
   "source": [
    "a.STATUS = 'cool'\n",
    "print(a.STATUS)\n",
    "print(b.STATUS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "may: 70\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'B'"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# i.e. \n",
    "class Student():\n",
    "    # 方法  self指向创建的实例本身\n",
    "    def __init__(self,name,score):\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "\n",
    "    def print_score(self):\n",
    "        print('{}: {}'.format(self.name,self.score))\n",
    "\n",
    "    def get_grade(self):\n",
    "        if self.score >= 90:\n",
    "            return 'A'\n",
    "        elif self.score >= 60:\n",
    "            return 'B'\n",
    "        else:\n",
    "            return 'C'\n",
    "\n",
    "# 实例化\n",
    "may = Student('may',70)\n",
    "may.print_score()\n",
    "may.get_grade()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "number of students: 2\n",
      "test succeed!\n"
     ]
    }
   ],
   "source": [
    "# 练习：统计学生人数 \n",
    "# 定义一个学生类，每初始化一次，学生人数加1； \n",
    "# 思路：可以给Student类增加一个类变量，每创建一个实例，该属性自动增加\n",
    "\n",
    "class Student:\n",
    "    count=0\n",
    "    \n",
    "    def __init__(self,name):\n",
    "        self.name = name\n",
    "        Student.count += 1\n",
    "        \n",
    "# test\n",
    "may=Student('may')\n",
    "if Student.count !=1:\n",
    "    print('test failed')\n",
    "\n",
    "june = Student('june')\n",
    "if Student.count!=2:\n",
    "    print('test failed')\n",
    "else:\n",
    "    print('number of students:',Student.count)\n",
    "    print('test succeed!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 实例方法，类方法和静态方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 实例方法\n",
    "\n",
    "- 对象/实例调用实例方法时会自动传入self参数，self为对象本身\n",
    "- 实例方法只能由对象调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "instance method\n",
      "--------------------\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "fun1() missing 1 required positional argument: 'self'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_14696/3619437910.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[0mi\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfun1\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'-'\u001b[0m\u001b[1;33m*\u001b[0m\u001b[1;36m20\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m \u001b[0mI\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfun1\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: fun1() missing 1 required positional argument: 'self'"
     ]
    }
   ],
   "source": [
    "class I:    \n",
    "    def fun1(self):  #实例方法\n",
    "        print('instance method')\n",
    "\n",
    "# 初始化\n",
    "i=I()\n",
    "i.fun1()\n",
    "print('-'*20)\n",
    "I.fun1()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类方法\n",
    "\n",
    "- 当一个方法，被classmethod装饰的时候，第一个参数会转变为类本身，这样的方法叫类方法\n",
    "- 可以通过第一个参数是类本身还是实例对象来判断是不是类方法\n",
    "- 类方法可以被实例使用，并且被实例使用时，传入的第一个参数还是类。\n",
    "- 类方法无需实例化就可以执行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "class I:\n",
    "    \n",
    "    def fun1(self):\n",
    "        print('instance method')\n",
    "        \n",
    "    @classmethod  # 装饰器\n",
    "    def class_fun1(cls):  # cls 是类本身\n",
    "        print('class method')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class method\n",
      "class method\n"
     ]
    }
   ],
   "source": [
    "I.class_fun1()\n",
    "i=I()\n",
    "i.class_fun1()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 静态方法\n",
    "\n",
    "- 当一个方法被staticmethod装饰时，不会自动传递第一个参数，这样的方法叫静态方法\n",
    "- 静态方法可以被实例和类调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "class I:\n",
    "    \n",
    "    def fun1(self):\n",
    "        print('instance method')\n",
    "        \n",
    "    @classmethod  # 装饰器\n",
    "    def class_fun1(cls):  # cls 是类本身\n",
    "        print('class method')\n",
    "        \n",
    "    @staticmethod \n",
    "    def static_fun1(): # 对于第一个参数没有实质要求 \n",
    "        print('static_method')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "static_method\n",
      "static_method\n"
     ]
    }
   ],
   "source": [
    "I.static_fun1()\n",
    "i=I()\n",
    "i.static_fun1()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "class I:\n",
    "    \n",
    "    def fun1(self):\n",
    "        print('instance method')\n",
    "        \n",
    "    @classmethod  # 装饰器\n",
    "    def class_fun1(cls):  # cls 是类本身\n",
    "        print('class method')\n",
    "        \n",
    "    @staticmethod \n",
    "    def static_fun1(): # 对于第一个参数没有实质要求 \n",
    "        print('static_method')\n",
    "\n",
    "    def x_fun1():\n",
    "        print('x print')\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x print\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "x_fun1() takes 0 positional arguments but 1 was given",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_14696/2169972780.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mI\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mx_fun1\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[0mi\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mI\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0mi\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mx_fun1\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: x_fun1() takes 0 positional arguments but 1 was given"
     ]
    }
   ],
   "source": [
    "I.x_fun1()\n",
    "i=I()\n",
    "i.x_fun1()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- staticmethod 和 x_fun1 的区别：实例不能调用，因为默认是会传入一个实例对象"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 总结：\n",
    "  - 对象可以访问 实例方法，类方法，静态方法\n",
    "  - 类可以访问 类方法，静态方法  \n",
    "  - 如果有的方法不需要实例化，则可以使用类方法  \n",
    "  - 静态方法非实例和类必须，只是借了一个命名空间的地方"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类的继承"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Base:\n",
    "    def base_print(self):\n",
    "        print('base')       \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 想让A拥有 base_print的方法\n",
    "class A(Base):\n",
    "    def a_print(self):\n",
    "        print('a')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "base\n"
     ]
    }
   ],
   "source": [
    "a=A()\n",
    "a.base_print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 继承的基本写法： \n",
    "- 括号里的类称为父类、基类或者超类\n",
    "- 继承可以获得父类的属性和方法(公有），说明代码可以重复利用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "# i.e. \n",
    "class Animal():\n",
    "    def run(self):\n",
    "        print('Animal is running')\n",
    "\n",
    "class Dog(Animal):\n",
    "    pass\n",
    "\n",
    "class Cat(Animal):\n",
    "    pass\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal is running\n",
      "Animal is running\n"
     ]
    }
   ],
   "source": [
    "dog=Dog()\n",
    "cat=Cat()\n",
    "dog.run()\n",
    "cat.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog is running\n"
     ]
    }
   ],
   "source": [
    "# 子类可以重写父类的方法\n",
    "class Dog(Animal):\n",
    "    def run(self):\n",
    "        print('Dog is running')\n",
    "\n",
    "dog=Dog()\n",
    "dog.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal is running\n",
      "Dog is running\n"
     ]
    }
   ],
   "source": [
    "# 重写父类方法时，调用父类的run方法：super().run()\n",
    "class Dog(Animal):\n",
    "    def run(self):\n",
    "        super().run()\n",
    "        print('Dog is running')\n",
    "        \n",
    "dog=Dog()\n",
    "dog.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Cat(Animal):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 访问限制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self,name,score):\n",
    "        self.__name = name  #__name表示私有属性\n",
    "        self.__score = score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Student' object has no attribute '__score'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-75-aedd361794a9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0ma\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mStudent\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m30\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0ma\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__score\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: 'Student' object has no attribute '__score'"
     ]
    }
   ],
   "source": [
    "a=Student('a',30)\n",
    "a.__score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 输出私有属性\n",
    "class Student(object):\n",
    "    \n",
    "    def __init__(self,name,score):\n",
    "        self.__name = name  #__name表示私有属性\n",
    "        self.__score = score\n",
    "        \n",
    "    def print_score(self):\n",
    "        return self.__score\n",
    "\n",
    "    def get_grade(self):\n",
    "        if self.__score>=90:\n",
    "            return 'A'\n",
    "        elif 60<=self.__score<9:\n",
    "            return 'B'\n",
    "        else:\n",
    "            return 'fail'\n",
    "\n",
    "    def set_score(self,score):\n",
    "        if 0<=score<=100:\n",
    "            self.__score=score\n",
    "        else:\n",
    "            raise ValueError('score should be [0,100]!')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "70"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "may=Student('may',50)\n",
    "may.set_score(70)\n",
    "may.print_score()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "90\n"
     ]
    }
   ],
   "source": [
    "# python没有真正的是私有，把私有改名称_Student__name\n",
    "xiaochen=Student('xiaochen',90)\n",
    "print(xiaochen._Student__score)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
